package com.pai4j.pgc.service.community;
import com.google.common.collect.Lists;
import com.pai4j.common.bean.PAIPageResponseBeanUtil;
import com.pai4j.common.constants.MessageConstants;
import com.pai4j.common.constants.WebConstant;
import com.pai4j.common.enums.CommonStatusEnum;
import com.pai4j.common.enums.RedisKeyEnum;
import com.pai4j.common.enums.community.ResourceBehaviorTypeEnum;
import com.pai4j.common.enums.community.ResourceTypeEnum;
import com.pai4j.common.util.*;
import com.pai4j.domain.community.vo.request.CCVideoSaveRequestVO;
import com.pai4j.domain.community.vo.response.cc.CTCPathDetailVO;
import com.pai4j.domain.community.vo.response.cc.CTCVideoBaseVO;
import com.pai4j.domain.community.vo.response.cc.CTCVideoDetailVO;
import com.pai4j.domain.community.vo.response.cc.CTCVideoListVO;
import com.pai4j.domain.community.vo.response.message.MessageResourceVO;
import com.pai4j.domain.vo.dto.SystemMessageDTO;
import com.pai4j.domain.vo.response.UserBaseResponseInfoVO;
import com.pai4j.pgc.dao.community.ICCVideoDAO;
import com.pai4j.pgc.dao.community.ICoderUtilUserDAO;
import com.pai4j.pgc.entity.community.CCVideoEntity;
import com.pai4j.pgc.entity.community.UserEntity;
import com.pai4j.pgc.service.behavior.AbstractResourceBehaviorService;
import com.pai4j.pgc.service.behavior.ResourceBehaviorFactory;
import com.pai4j.pgc.service.event.CourseStudyEvent;
import com.pai4j.pgc.service.message.builder.SystemMessageBuilder;
import com.pai4j.pgc.service.queue.UserMessageQueue;
import com.pai4j.remote.user.UserServiceClient;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 技术社区视频
 *
 * @author: CYM-pai
 * @date: 2025/10/26 21:22
 **/
@Slf4j
@Service
public class CCVideoService implements ResourceMessageInterface {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ICCVideoDAO videoDAO;
    @Autowired
    private UserServiceClient userServiceClient;
    @Autowired
    private CCPathService pathService;
    @Autowired
    private UserMessageQueue userMessageQueue;
    @Autowired
    private CTCScheduleService ctcScheduleService;
    @Autowired
    private ICoderUtilUserDAO userDAO;
    @Autowired
    private CCVideoSummaryService videoSummaryService;
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 发布/编辑问题
     *
     * @param userId
     * @return
     */
    public Long save(CCVideoSaveRequestVO videoSaveRequest, String userId) {
        CCVideoEntity video = this.convertEntity(videoSaveRequest, userId);
        video = videoDAO.save(video);
        CCVideoEntity finalVideo = video;
        ThreadPoolExecutorUtil.execute(() -> {
            // 刷新最新问题列表缓存
            addCCNewQuestionCache(finalVideo.getPathId(), finalVideo.getId(), finalVideo.getCreateDate().getTime());
            // 刷新问题详情缓存
            refreshQuestionDetailCache(finalVideo);
            if (finalVideo.getId() == null) {
                // 首次提交问题，ai回复
                aiAutoComment(finalVideo);
            }
            CTCPathDetailVO pathDetailVO = pathService.getPathBaseFromCache(finalVideo.getPathId());
            if (CommonStatusEnum.PUBLISHED.getStatus().equals(finalVideo.getStatus())) {
                if (userServiceClient.isAdmin(finalVideo.getAuthor())) {
                    // 发布课程内容通知
                    AbstractResourceBehaviorService behaviorService = ResourceBehaviorFactory.getBehaviorService(ResourceBehaviorTypeEnum.FOCUS.getBehavior());
                    Set<String> focusUsers = behaviorService.getBehaviorUsersIdFromCache(ResourceTypeEnum.CTC_PATH.getType(), finalVideo.getPathId());
                    if (CollectionUtils.isNotEmpty(focusUsers)) {
                        List<List<String>> focusUserList =  Lists.partition(new ArrayList<>(focusUsers), 200);
                        for (List<String> focusUserIdList : focusUserList) {
                            List<UserEntity> userEntities = userDAO.findByUserIdIn(focusUserIdList);
                            Set<UserEntity> focusWeixinUsers = userEntities.stream().filter(u -> StringUtils.isNotBlank(u.getWeixinOpenId())).collect(Collectors.toSet());
                        }
                    }
                }
            }
        });
        return finalVideo.getId();
    }

    private void aiAutoComment(CCVideoEntity video) {
//        String title = video.getTitle();
//        String description = video.getDescription();
//        boolean isAuto = !AI_ROBOT_USER_ID.equals(video.getAuthor());
//        if (!isAuto) {
//            return;
//        }
//        CTCPathDetailVO star = pathService.getPathBaseFromCache(video.getPathId());
//        UserBaseResponseInfoVO user = userService.getUserBaseInfoByUserId(video.getAuthor());
//        Map<String, Object> vars = new HashMap<>();
//        vars.put("starName", star.getName());
//        vars.put("userName", user.getUserName());
//        vars.put("title", title);
//        vars.put("description", description == null ? "" : description);
//        String prompt = null;
//        try {
//            prompt = freeMarkEngineService.getContentByTemplate(PromptTemplateEnum.STAR_KEFU.getName(), vars);
//            ChatRequestVO chatRequest = new ChatRequestVO();
//            chatRequest.setUserId(video.getAuthor());
//            chatRequest.setText(prompt);
//            ChatResponseVO chatResponseVO = ernieBot4ChatService.sync(chatRequest);
//            String reply = chatResponseVO.getResponse();
//            if (StringUtils.isBlank(reply)) {
//                return;
//            }
//            CCAnswerSaveRequestVO answerSaveRequest = new CCAnswerSaveRequestVO();
//            answerSaveRequest.setQuestionId(qa.getId());
//            answerSaveRequest.setContent("<div style='padding-top: 12px'>" + reply.replaceAll("\n", "<br/>") + "</div>");
//            answerService.save(answerSaveRequest, AI_ROBOT_USER_ID);
//        } catch (Exception e) {
//            log.error("===>aiAutoComment error.", e);
//        }
    }

    public boolean delete(Long videoId, String userId) {
        CCVideoEntity video = videoDAO.findById(videoId).orElse(null);
        Assert.isTrue(video != null, "问题不存在！");
        Assert.isTrue(!video.getStatus().equals(CommonStatusEnum.DELETED.getStatusCode()), "视频已删除！");
        boolean hasDeleteAuth = userServiceClient.isAdmin(userId) || video.getAuthor().equals(userId);
        Assert.isTrue(hasDeleteAuth, "无删除权限！");
        video.setStatus(CommonStatusEnum.DELETED.getStatusCode());
        video.setUpdateBy(userId);
        video.setUpdateDate(new Date());
        videoDAO.save(video);
        // 刷新缓存
        ThreadPoolExecutorUtil.execute(() -> this.deleteQuestionByCache(video.getPathId(), videoId));
        return true;
    }

    private void deleteQuestionByCache(Long pathId, Long id) {
        this.refreshQuestionDetailCache(id);
        // TODO 刷新视频
        ctcScheduleService.refreshCTCHomeQuestionList(pathId);
        initCTCNewQuestionCache(pathId);
    }

    /**
     * 设置经验推荐
     * @param id
     * @param status
     * @param userId
     */
    public void setRecommend(Long id, Boolean status, String userId) {
        CCVideoEntity videoEntity = videoDAO.findById(id).orElse(null);
        Assert.isTrue(videoEntity != null, "操作失败:视频不存在!");
        videoEntity.setRecommend(status);
        videoEntity.setRecommendDate(status ? new Date() : null);
        videoEntity.setUpdateBy(userId);
        videoEntity.setUpdateDate(new Date());
        videoEntity = videoDAO.save(videoEntity);
        /**
         * 刷新置顶经验缓存
         */
        this.refreshQuestionDetailCache(videoEntity);
        this.setQuestionRecommendListCache(videoEntity.getPathId(), id, status);
        if (ObjectUtils.equals(status, true)) {
            // 加精，积分奖励
            SystemMessageBuilder systemMessageBuilder = new SystemMessageBuilder();
            SystemMessageDTO systemMessage = systemMessageBuilder.buildResourceMessage(
                    MessageConstants.TypeEnum.RECOMMEND,
                    userId,
                    "您在社区发布的视频《"+videoEntity.getTitle()+"》被推荐精华，感谢您的参与，积分奖励已到账！",
                    ResourceTypeEnum.CTC_VIDEO,
                    String.valueOf(id));
            userMessageQueue.submit(systemMessage);
        }
    }

    /**
     * 设置推荐经验缓存
     * @param pathId
     * @param id
     * @param status
     */
    private void setQuestionRecommendListCache(Long pathId, Long id, Boolean status) {
        String key = RedisKeyEnum.CTC_RECOMMEND_VIDEO_ZSET_CACHE.getKey(pathId.toString());
        if (status) {
            redisUtil.zadd(key, id.toString(), DateUtils.getCurrentTimeMillis(), RedisKeyEnum.CTC_RECOMMEND_VIDEO_ZSET_CACHE.getExpireTime());
        } else {
           redisUtil.zremove(key, id.toString());
        }
    }

    public void updateFree(Long id, String userId) {
        CCVideoEntity video = videoDAO.findById(id).orElse(null);
        Assert.isTrue(video != null, "状态更新失败，资源不存在");
        boolean free = video.getFree();
        video.setFree(!free);
        video.setUpdateBy(userId);
        video.setUpdateDate(new Date());
        videoDAO.save(video);
        ThreadPoolExecutorUtil.execute(() -> {
            refreshQuestionDetailCache(video);
        });
    }

    public CTCVideoDetailVO getPermissionQuestionDetailFromCache(Long videoId, String currUserId) {
        CTCVideoDetailVO video = this.getQuestionDetailFromCache(videoId, currUserId);
        if (ObjectUtils.equals(video.getVideo().getFree(), true)) {
            return video;
        }

        // 校验社区订阅权限
        pathService.validatePathPermission(currUserId, video.getPath().getId());
        // 纪录学习历史
        CourseStudyEvent courseStudyEvent = new CourseStudyEvent(this, currUserId, ResourceTypeEnum.CTC_VIDEO, videoId);
        applicationContext.publishEvent(courseStudyEvent);
        return video;
    }

    /**
     * 查询问题详情
     * @param videoId
     * @param currUserId
     * @return
     */
    public CTCVideoDetailVO getQuestionDetailFromCache(Long videoId, String currUserId) {
        CTCVideoDetailVO video = new CTCVideoDetailVO();
        CTCVideoBaseVO videoBase = getQuestionBaseFromCache(videoId);
        if (videoBase == null) {
            return video;
        }
        if (StringUtils.isNotBlank(videoBase.getDescription())) {
            videoBase.setDescription(HtmlUtil.xssEscape(videoBase.getDescription()).replaceAll("\n", "<br/>"));
        }
        video.setVideo(videoBase);
        video.setAuthorInfo(userServiceClient.getUserInfoByUserId(videoBase.getAuthor()));
        video.setPath(pathService.getPathBaseFromCache(videoBase.getPathId(), currUserId));
        video.setViewCount(addQuestionViewCountCacheAndDB(videoId));
        video.setAnswerCount(getQuestionAnswerCountFromCache(videoId));

        AbstractResourceBehaviorService likeService =
                ResourceBehaviorFactory.getBehaviorService(ResourceBehaviorTypeEnum.LIKE.getBehavior());
        Map<Long, Long> likeMap = likeService.getBehaviorCountFromCache(ResourceTypeEnum.CTC_VIDEO.getType(), Arrays.asList(videoId));
        video.setLikeCount(likeMap.getOrDefault(videoId, 0L));
        if (StringUtils.isNotBlank(currUserId)) {
            Map<Long, Boolean> isLikeMap = likeService.getIsBehaviorFromCache(ResourceTypeEnum.CTC_VIDEO.getType(), Arrays.asList(videoId), currUserId);
            video.setIsLike(isLikeMap.getOrDefault(videoId, false));
            AbstractResourceBehaviorService attendService =
                    ResourceBehaviorFactory.getBehaviorService(ResourceBehaviorTypeEnum.COLLECT.getBehavior());
            Map<Long, Boolean> isCollectMap = attendService.getIsBehaviorFromCache(ResourceTypeEnum.CTC_VIDEO.getType(), Arrays.asList(videoId), currUserId);
            video.setIsCollect(isCollectMap.getOrDefault(videoId, false));
        }
        boolean manager = userServiceClient.isAdmin(currUserId) || videoBase.getAuthor().equals(currUserId);
        video.setManager(manager);
        video.setHasSummary(videoSummaryService.hasSummary(videoId));
        return video;
    }

       /**
     * 管理员视频列表查询
     *
     * @param pathId     路径ID，用于筛选指定路径下的视频数据
     * @param kw         关键词，用于模糊匹配视频标题或相关内容
     * @param top        是否置顶标识，用于筛选置顶状态的视频
     * @param recommend  是否推荐标识，用于筛选推荐状态的视频
     * @param status     视频状态，用于筛选特定状态（如上线、下线等）的视频
     * @param pageNo     当前页码，从1开始计数
     * @param pageSize   每页显示条数
     * @return 分页结果封装对象，包含当前页数据及总记录数
     */
    public PAIPageResponseBeanUtil<CTCVideoListVO> adminPageList(Long pathId, String kw, Boolean top, Boolean recommend,
                                                                 Integer status, int pageNo, int pageSize) {
        // 构建分页请求对象，按ID倒序排列
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Order.desc("id")));

        // 构造查询条件
        Specification<CCVideoEntity> questionEntitySpecification = buildQuestionQueryCondition(pathId, kw, top, recommend, status);

        // 执行数据库查询获取分页数据
        Page<CCVideoEntity> videoEntityPage = videoDAO.findAll(questionEntitySpecification, pageable);

        // 若无数据，直接返回空列表结果
        if (CollectionUtils.isEmpty(videoEntityPage.getContent())) {
            return PAIPageResponseBeanUtil.success(pageNo, pageSize, videoEntityPage.getTotalElements(), Collections.emptyList());
        }

        // 提取视频实体列表及相关字段信息
        List<CCVideoEntity> videoEntities = videoEntityPage.getContent();
        List<Long> qidList = videoEntities.stream().map(CCVideoEntity::getId).collect(Collectors.toList());
        List<String> authors = videoEntities.stream().map(CCVideoEntity::getAuthor).collect(Collectors.toList());

        // 批量获取作者用户信息和浏览量统计
        Map<String, UserBaseResponseInfoVO> authorMap = userServiceClient.batchGetUserInfoFromCache(authors);
        Map<Long, Long> viewCountMap = batchGetQuestionViewCount(qidList);

        // 将视频实体转换为展示层VO对象，并填充相关信息
        List<CTCVideoListVO> questionListVOList = videoEntities.stream().map(question -> {
            CTCVideoListVO videoListVO = new CTCVideoListVO();
            CTCVideoBaseVO videoBaseVO = convertCTCQuestionDetailVO(question);
            videoBaseVO.setDescription(null); // 描述字段设为空
            videoListVO.setVideo(videoBaseVO);
            videoListVO.setAuthorInfo(authorMap.get(question.getAuthor()));
            videoListVO.setPath(pathService.getCTCPathDetailVOFromCache(question.getPathId()));
            videoListVO.setAnswerCount(0L); // 回答数量默认为0
            videoListVO.setViewCount(viewCountMap.getOrDefault(question.getId(), 0L)); // 设置浏览次数
            return videoListVO;
        }).collect(Collectors.toList());

        // 返回成功响应结果
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, videoEntityPage.getTotalElements(), questionListVOList);
    }


    private Specification<CCVideoEntity> buildQuestionQueryCondition(Long pathId, String kw, Boolean top,
                                                                        Boolean recommend, Integer status) {
        Specification<CCVideoEntity> specification = new Specification<CCVideoEntity>() {
            @Override
            public Predicate toPredicate(Root<CCVideoEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                if (pathId != null) {
                    predicates.add(criteriaBuilder.equal(root.get("pathId").as(Long.class), pathId));
                }
                if (status != null) {
                    predicates.add(criteriaBuilder.equal(root.get("status").as(Integer.class), status));
                }
                if (recommend != null) {
                    predicates.add(criteriaBuilder.equal(root.get("recommend").as(Boolean.class), recommend));
                }
                if (StringUtils.isNotBlank(kw)) {
                    predicates.add(
                            criteriaBuilder.or(
                                    criteriaBuilder.like(root.get("title").as(String.class), "%" + kw + "%")));
                }
                Predicate[] pre = new Predicate[predicates.size()];
                criteriaQuery.where(predicates.toArray(pre));
                return criteriaQuery.getRestriction();
            }
        };
        return specification;
    }


    public PAIPageResponseBeanUtil<List<CTCVideoListVO>> pageList(String typeList, Long pathId, int pageNo, int pageSize) {
        Set<String> questionIdSet = null;
        Long total = 0L;
        if ("new".equals(typeList)) {
            questionIdSet = getNewQuestionListFromCache(pathId, pageNo, pageSize);
            total = getNewQuestionTotalFromCache(pathId);
        }
        List<CTCVideoListVO> list = new ArrayList<>();
        if (CollectionUtils.isEmpty(questionIdSet)) {
            return PAIPageResponseBeanUtil.success(pageNo, pageSize, total, list);
        }
        List<Long> qidList = questionIdSet.stream().map(Long::valueOf).collect(Collectors.toList());
        /**
         * 批量查询
         */
        List<CTCVideoBaseVO> videoBaseVOList = this.batchGetQuestionListFromCache(qidList);
        List<String> authors = videoBaseVOList.stream().map(CTCVideoBaseVO::getAuthor).collect(Collectors.toList());
        Map<Long, Long> viewCountMap = this.batchGetQuestionViewCount(qidList);
        Map<String, UserBaseResponseInfoVO> authorMap = userServiceClient.batchGetUserInfoFromCache(authors);
        for (CTCVideoBaseVO videoBaseVO : videoBaseVOList) {
            CTCVideoListVO videoListVO = new CTCVideoListVO();
            videoListVO.setVideo(videoBaseVO);
            videoListVO.setViewCount(viewCountMap.get(videoBaseVO.getId()));
            videoListVO.setAnswerCount(0L);
            videoListVO.setAuthorInfo(authorMap.get(videoBaseVO.getAuthor()));
            list.add(videoListVO);
        }
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, total, list);
    }


    public Set<String> getNewQuestionListFromCache(Long pathId, int pageNo, int pageSize) {
        String key = RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getKey(pathId.toString());
        if (!isExistKey(key)) {
            // 初始化社区最新问题缓存
            this.initCTCNewQuestionCache(pathId);
        }
        return redisUtil.zreverseRange(key, (pageNo - 1) * pageSize, pageNo * pageSize);
    }

    /**
     * 查询问题数量
     * @param pathId
     * @return
     */
    public Long getQuestionCountFromCache(Long pathId) {
        String key = RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getKey(pathId.toString());
        if (!isExistKey(key)) {
            // 初始化社区最新问题缓存
            this.initCTCNewQuestionCache(pathId);
        }
        return redisUtil.zsize(key);
    }

    public Long getNewQuestionTotalFromCache(Long pathId) {
        String key = RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getKey(pathId.toString());
        return redisUtil.zSetSize(key);
    }

    /**
     * redis + MYSQL 双写记录浏览量
     * @param videoId
     * @return
     */
    private Long addQuestionViewCountCacheAndDB(Long videoId) {
        String key = RedisKeyEnum.CTC_VIDEO_VIEW_COUNT_CACHE.getKey();
        String field = String.valueOf(videoId);
        Long count = redisUtil.hincrex(key, field);
        if (count == 1) {
            CCVideoEntity video = videoDAO.findById(videoId).orElse(null);
            count = video.getViewCount() + 1;
            video.setViewCount(count);
            videoDAO.save(video);
           redisUtil.hset(key, field, String.valueOf(count), RedisKeyEnum.CTC_VIDEO_VIEW_COUNT_CACHE.getExpireTime());
        } else {
            ThreadPoolExecutorUtil.execute(() -> videoDAO.increxVideoViewCount(videoId));
        }
        return count;
    }

    public Long getQuestionAnswerCountFromCache(Long questionId) {

        return 0L;
    }

    private Long refreshQuestionViewCountCacheFromDB(Long questionId) {
        String key = RedisKeyEnum.CTC_VIDEO_VIEW_COUNT_CACHE.getKey();
        CCVideoEntity video = videoDAO.findById(questionId).orElse(null);
        if (video == null) {
            return 0L;
        }
        Long viewCount = video.getViewCount();
        String field = String.valueOf(questionId);
        redisUtil.hset(key, field, String.valueOf(viewCount), RedisKeyEnum.CTC_VIDEO_VIEW_COUNT_CACHE.getExpireTime());
        return viewCount;
    }

    public Map<Long, Long> batchGetQuestionViewCount(List<Long> qidList) {
        if (CollectionUtils.isEmpty(qidList)) {
            return Collections.emptyMap();
        }
        Map<Long, Long> viewCountMap = new HashMap<>();
        List<String> fields = qidList.stream().map(String::valueOf).collect(Collectors.toList());
        String key = RedisKeyEnum.CTC_VIDEO_VIEW_COUNT_CACHE.getKey();
        List<String> viewCountList = redisUtil.hmget(key, fields);
        for (int i = 0; i < qidList.size(); i++) {
            Long qid = qidList.get(i);
            String viewCountStr = viewCountList.get(i);
            Long viewCount = 0L;
            if (StringUtils.isNotBlank(viewCountStr)) {
                viewCount = Long.valueOf(viewCountStr);
            } else {
                viewCount = refreshQuestionViewCountCacheFromDB(qid);
            }
            viewCountMap.put(qid, viewCount);
        }
        return viewCountMap;
    }

    /**
     * 添加最新问题进入缓存
     *
     * @param pathId
     * @param videoId
     */
    private void addCCNewQuestionCache(Long pathId, Long videoId, Long time) {
        String key = RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getKey(pathId.toString());
        if (!isExistKey(key)) {
            // 初始化社区最新问题缓存
            this.initCTCNewQuestionCache(pathId);
            return;
        }
        redisUtil.zadd(key, String.valueOf(videoId), time, RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getExpireTime());
    }

    /**
     * 删除最新问题缓存中的指定栏目
     *
     * @param questionId
     */
    private void removeCCNewQuestionCache(Long pathId, Long questionId) {
        String key = RedisKeyEnum.CTC_QUESTION_NEW_ZSET_CACHE.getKey(pathId.toString());
        if (!isExistKey(key)) {
            // 初始化社区最新栏目缓存
            this.initCTCNewQuestionCache(pathId);
            return;
        }
        redisUtil.zremove(key, String.valueOf(questionId));
    }

    public void initCTCNewQuestionCache(Long pathId) {
        String key = RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getKey(pathId.toString());
        redisUtil.remove(key);
        List<CCVideoEntity> videoEntities = videoDAO.findAllByPathIdAndStatusNotOrderByIdDesc(
                pathId, CommonStatusEnum.DELETED.getStatusCode());
        if (CollectionUtils.isEmpty(videoEntities)) {
            return;
        }
        Set<ZSetOperations.TypedTuple<String>> tuples = videoEntities.stream().map(video -> {
            String value = String.valueOf(video.getId());
            Double score = Double.valueOf(video.getCreateDate().getTime());
            return new DefaultTypedTuple<>(value, score);
        }).collect(Collectors.toSet());
        redisUtil.zadd(key, tuples, RedisKeyEnum.CTC_VIDEO_NEW_ZSET_CACHE.getExpireTime());
    }

    private boolean isExistKey(String key) {
        return redisUtil.exists(key);
    }

    /**
     * 查询问题的baseinfo
     * @param videoId
     * @return
     */
    public CTCVideoBaseVO getQuestionBaseFromCache(Long videoId) {
        String key = RedisKeyEnum.CTC_VIDEO_DETAIL_CACHE.getKey(videoId.toString());
        String cache = redisUtil.get(key);
        if (WebConstant.CACHE_NONE.equals(cache)) {
            return null;
        }
        if (StringUtils.isBlank(cache)) {
            return refreshQuestionDetailCache(videoId);
        }
        return JsonUtil.fromJson(cache, CTCVideoBaseVO.class);
    }

    public List<CTCVideoBaseVO> batchGetQuestionListFromCache(List<Long> videoIdSet) {
        if (CollectionUtils.isEmpty(videoIdSet)) {
            return Collections.emptyList();
        }
        List<CTCVideoBaseVO> videoBaseVOList = new ArrayList<>(videoIdSet.size());
        List<String> keys = videoIdSet.stream().map(qid ->
                RedisKeyEnum.CTC_VIDEO_DETAIL_CACHE.getKey(qid.toString())).collect(Collectors.toList());
        List<String> caches = redisUtil.mget(keys);
        for (int i = 0; i < videoIdSet.size(); i++) {
            Long qid = videoIdSet.get(i);
            String cache = caches.get(i);
            CTCVideoBaseVO videoBaseVO;
            if (StringUtils.isNotBlank(cache)) {
                videoBaseVO = JsonUtil.fromJson(cache, CTCVideoBaseVO.class);
            } else {
                videoBaseVO = refreshQuestionDetailCache(qid);
            }
            if (videoBaseVO != null) {
                videoBaseVOList.add(videoBaseVO);
            }
        }
        return videoBaseVOList;
    }

    public Map<Long, CTCVideoBaseVO> batchGetQuestionMapFromCache(List<Long> questionIdSet) {
        if (CollectionUtils.isEmpty(questionIdSet)) {
            return Collections.emptyMap();
        }
        Map<Long, CTCVideoBaseVO> map = new HashMap<>(questionIdSet.size());
        List<String> keys = questionIdSet.stream().map(qid ->
                RedisKeyEnum.CTC_VIDEO_DETAIL_CACHE.getKey(qid.toString())).collect(Collectors.toList());
        List<String> caches = redisUtil.mget(keys);
        for (int i = 0; i < questionIdSet.size(); i++) {
            Long qid = questionIdSet.get(i);
            String cache = caches.get(i);
            CTCVideoBaseVO videoBaseVO;
            if (StringUtils.isNotBlank(cache)) {
                videoBaseVO = JsonUtil.fromJson(cache, CTCVideoBaseVO.class);
            } else {
                videoBaseVO = refreshQuestionDetailCache(qid);
            }
            if (videoBaseVO != null) {
                map.put(qid, videoBaseVO);
            }
        }
        return map;
    }

    private CTCVideoBaseVO refreshQuestionDetailCache(Long videoId) {
        CCVideoEntity video = videoDAO.findById(videoId).orElse(null);
        if (video == null) {
            return null;
        }
        return this.refreshQuestionDetailCache(video);
    }

    private CTCVideoBaseVO refreshQuestionDetailCache(CCVideoEntity question) {
        String key = RedisKeyEnum.CTC_VIDEO_DETAIL_CACHE.getKey(question.getId().toString());
        if (question == null) {
            redisUtil.set(key, WebConstant.CACHE_NONE, RedisKeyEnum.CTC_VIDEO_DETAIL_CACHE.getExpireTime());
            return null;
        }
        CTCVideoBaseVO videoBaseVO = this.convertCTCQuestionDetailVO(question);
        redisUtil.set(key, JsonUtil.toJsonString(videoBaseVO), RedisKeyEnum.CTC_VIDEO_DETAIL_CACHE.getExpireTime());
        return videoBaseVO;
    }

    private CTCVideoBaseVO convertCTCQuestionDetailVO(CCVideoEntity video) {
        CTCVideoBaseVO videoBaseVO = new CTCVideoBaseVO();
        BeanUtils.copyProperties(video, videoBaseVO);
        videoBaseVO.setSigns(ListUtil.string2List(video.getSigns()));
        video.setRecommend(ObjectUtils.equals(video.getRecommend(), true));
        if (video.getRecommendDate() != null) {
            videoBaseVO.setRecommendTime(video.getRecommendDate().getTime());
        }
        videoBaseVO.setCreateTime(video.getCreateDate().getTime());
        return videoBaseVO;
    }

    private CCVideoEntity convertEntity(CCVideoSaveRequestVO videoSaveRequestVO, String userId) {
        Long id = videoSaveRequestVO.getId();
        CCVideoEntity videoEntity;
        Date now = new Date();
        if (id != null) {
            videoEntity = videoDAO.findById(id).orElse(null);
            Assert.isTrue(videoEntity != null, "更新失败，视频不存在");
            Assert.isTrue(videoEntity.getAuthor().equals(userId), "无权限！");
        } else {
            videoEntity = new CCVideoEntity();
            videoEntity.setPathId(videoSaveRequestVO.getPathId());
            videoEntity.setAuthor(userId);

            videoEntity.setCreateBy(userId);
            videoEntity.setCreateDate(now);
            videoEntity.setViewCount(0L);
            videoEntity.setFree(false);
        }
        videoEntity.setStatus(CommonStatusEnum.PUBLISHED.getStatusCode());
        videoEntity.setCover(videoSaveRequestVO.getCover());
        videoEntity.setUpdateBy(userId);
        videoEntity.setUpdateDate(now);
        videoEntity.setTitle(videoSaveRequestVO.getTitle());
        videoEntity.setDescription(videoSaveRequestVO.getDescription());
        videoEntity.setSigns(videoSaveRequestVO.getSigns());
        videoEntity.setVideo(videoSaveRequestVO.getVideo());
        return videoEntity;
    }

    @Override
    public Map<String, MessageResourceVO> batchGetMessResource(List<String> resourceIdList) {
        Map<String, MessageResourceVO> map = new HashMap<>();
        List<Long> ids = resourceIdList.stream().map(Long::valueOf).collect(Collectors.toList());
        Map<Long, CTCVideoBaseVO> questionBaseVOMap = batchGetQuestionMapFromCache(ids);
        for (String resourceId : resourceIdList) {
            CTCVideoBaseVO video = questionBaseVOMap.get(Long.valueOf(resourceId));
            if (video != null) {
                MessageResourceVO messageResource = new MessageResourceVO();
                messageResource.setId(resourceId);
                messageResource.setTitle(video.getTitle());
                messageResource.setCover(video.getVideo()+"?x-oss-process=video/snapshot,t_1000,f_jpg,w_80,h_80,m_fast");
                messageResource.setAuthor(video.getAuthor());
                messageResource.setModule(ResourceTypeEnum.CTC_VIDEO.getTypeName());
                messageResource.setType(ResourceTypeEnum.CTC_VIDEO.getType());
                map.put(resourceId, messageResource);
            }
        }
        return map;
    }
}
